package com.yun.zhong.fu.sharecenter.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.yun.zhong.fu.sharecenter.dao.MidUserShareDao;
import com.yun.zhong.fu.sharecenter.dao.RocketLogDao;
import com.yun.zhong.fu.sharecenter.dao.ShareDao;
import com.yun.zhong.fu.sharecenter.domain.ShareDto;
import com.yun.zhong.fu.sharecenter.domain.UserBonusDto;
import com.yun.zhong.fu.sharecenter.domain.UserDto;
import com.yun.zhong.fu.sharecenter.domain.message.UserAddBonusMsgDTO;
import com.yun.zhong.fu.sharecenter.entity.MidUserShare;
import com.yun.zhong.fu.sharecenter.entity.RocketMqTranscationLog;
import com.yun.zhong.fu.sharecenter.entity.Share;
import com.yun.zhong.fu.sharecenter.feginclinet.UserCenterFeignClinet;
import com.yun.zhong.fu.sharecenter.rocketmq.RocketMqProductor;
import com.yun.zhong.fu.sharecenter.utils.AuditEnum;
import com.yun.zhong.fu.sharecenter.utils.SharedAuditDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @Author RenPu
 * @Date 2020/12/28 16:21
 * @Version 1.0
 * @Description:
 **/
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ShareService {

//    private final RestTemplate restTemplate;

    private final UserCenterFeignClinet userCenterFeignClinet;

    //获取nacos上的服务列表
    private final DiscoveryClient discoveryClient;

    private final ShareDao shareDao;


    private final RocketLogDao rocketLogDao;

    //装配rockettemplate
    private final RocketMQTemplate rocketMQTemplate;


    private final MidUserShareDao midUserShareDao;

    private final RocketMqProductor rocketMqProductor;


    /**
     * @param id
     * @return
     */
    public ShareDto getById(Integer id) {
        Share share = this.shareDao.selectByPrimaryKey(id);
        Integer id1 = share.getId();


        //获取nacos上的所有服务实例
        List<ServiceInstance> instances = this.discoveryClient.getInstances("user-center");

//未实现负载均衡的编写模式，可以固定进行调用
//        String targetUrl=instances.stream()
//                //数据转变
//                .map(instance -> instance.getUri().toString()+"/user/{id}")
//                .findFirst()
//                .orElseThrow(()-> new IllegalArgumentException("当前没有服务实例---o(╥﹏╥)o"));

//------------------------------------------------------------------------------------------------------
//        (通过随机算法实现负载均衡）
//        获取所有实例的url的集合
//        List<String> targetUrls = instances.stream().map(instance -> instance.getUri().toString() + "/user/{id}").collect(Collectors.toList());

        //通过随机算法，获取任意一个实例
//        int i = ThreadLocalRandom.current().nextInt(targetUrls.size());
//
//        String denstionUrl=targetUrls.get(i);
//        UserDto UserDto = this.restTemplate.getForObject(denstionUrl, UserDto.class, id1);
        //  log.info("请求的目标地址："+denstionUrl);

        //UserDto UserDto = this.restTemplate.getForObject("http://user-center/user/{id}", UserDto.class, id1);

        UserDto UserDto = this.userCenterFeignClinet.byIdUser(id1);
        ShareDto shareDto = new ShareDto();
        BeanUtils.copyProperties(share, shareDto);
        shareDto.setWxNickname(UserDto.getWxNickname());
        return shareDto;
    }


    /**
     * @param id
     * @param sharedAuditDto
     * @return
     */
    public Share auditById(Integer id, SharedAuditDto sharedAuditDto) {
        //1:根据唯一的ID去获取share，是否存在。不存在或者audit_status !=NOT_YET，那么抛出异常

        Share share = this.shareDao.selectByPrimaryKey(id);
        if (share == null) {
            throw new IllegalArgumentException("参数非法，该分享不存在！！");
        }
        if (!Objects.equals("NOT_YET", share.getAuditStatus())) {
            throw new IllegalArgumentException("参数非法，该分享已经通过审核！！");
        }
        //2：审核资源，更改审核的状态为pass/reject
        share.setAuditStatus(sharedAuditDto.getAuditStatusEnum().toString());
        this.shareDao.updateByPrimaryKey(share);

        //3.1如果是PASS，那么向RocketMq发送半消息，让用户中心去消费，并为发布人添加积分
        if (AuditEnum.PASS.equals(sharedAuditDto.getAuditStatusEnum())) {

            String transcationId = UUID.randomUUID().toString();

            //向rocketMq发送半消息 （消息生产者）
            rocketMQTemplate.sendMessageInTransaction(
                    "test-add-bonus",
                    "add-bonus",
                    MessageBuilder.withPayload(
                                    UserAddBonusMsgDTO
                                            .builder()
                                            .bounus(500)
                                            .userId(share.getUserId())
                                            .build())
                            .setHeader(RocketMQHeaders.TRANSACTION_ID, transcationId)
                            .setHeader("share_id", id)
                            .build(),
                    sharedAuditDto);
            rocketMqProductor.sentAvailableMsg(share, id, sharedAuditDto);
        } else {
            this.auditById(id, sharedAuditDto);
        }


        //3:如果是pass，那么为发布人添加积分
        //在此可以优化设计为异步调用，提高用户的体验流程注解 @Async来实现异步调用的流程
////        @Async
////         userCenterFeignClinet.addJiFen(id,500);
//        //add-bonus:主题的名称，payload：消息体
//        rocketMQTemplate.convertAndSend("add-bonus",
//                UserAddBonusMsgDTO
//                        .builder()
//                        .bounus(500)
//                        .userId(share.getUserId())
//                        .build()
//                        );

        //把share写缓存中；


        return share;
    }


    /**
     * 验证是否具备审核的状态或者加积分的状态
     *
     * @param id
     * @param sharedAuditDto
     */
    @Transactional(rollbackFor = Exception.class)
    public void auditByIdInDB(Integer id, SharedAuditDto sharedAuditDto) {
        Share share = Share.builder()
                .id(id)
                .auditStatus(sharedAuditDto.getAuditStatusEnum().toString())
                .reason(sharedAuditDto.getReason())
                .build();
        this.shareDao.updateByPrimaryKeySelective(share);
    }


    /**
     * 回查方法，为了避免事务的不一致性
     *
     * @param id
     * @param sharedAuditDto
     * @param transcationId
     */
    @Transactional(rollbackFor = Exception.class)
    public void auditByIdWithRocketMqLog(Integer id, SharedAuditDto sharedAuditDto, String transcationId) {
        this.auditByIdInDB(id, sharedAuditDto);
        this.rocketLogDao.insert(RocketMqTranscationLog
                .builder()
                .transcationId(transcationId)
                .log("审核分享.....")
                .build());


    }

    /**
     * @param pageNo
     * @param pageSize
     * @param title
     * @param userId
     * @return
     */
    public PageInfo<Share> page(Integer pageNo, Integer pageSize, String title, Integer userId) {

        //创建结果集封装的集合
        List<Share> shareDeal;

        //它会自动切入下面不分页的sql，自动拼接
        PageHelper.startPage(pageNo, pageSize);
        //不分页的sql
        List<Share> shares = this.shareDao.likeQueryData(title);
        //1:如果用户未登陆，所有的downloadUrl全部设为null
        if (userId == null) {
            shareDeal = shares.stream().peek(share -> {
                share.setDownloadUrl(null);
            }).collect(Collectors.toList());
        }
        //2:如果用户登陆了，那么查询一下mid_user_share,如果没有数据，那么这条share的downloadUrl也要设为null
        else {
            shareDeal = shares.stream().peek(share -> {
                MidUserShare midUserShare = this.midUserShareDao.selectOne(MidUserShare.builder()
                        .shareId(share.getId())
                        .userId(userId).build());
                if (midUserShare == null) {
                    share.setDownloadUrl(null);
                }
            }).collect(Collectors.toList());
        }
        return new PageInfo<Share>(shareDeal);
    }

    /**
     * 积分兑换指定ID的share
     *
     * @param id
     * @param request
     * @return
     */
    public Share exchangeById(Integer id, HttpServletRequest request) {

        //1:根据用户的id查询，用户的积分是否满足兑换所需的积分
        Integer userId = (Integer) request.getAttribute("id");

        //2:验证根据id查询，是否存在
        Share share = this.shareDao.selectByPrimaryKey(id);

        //获取文章兑换所需要的积分
        Integer peice = share.getPrice();
        if (share == null) {
            throw new IllegalArgumentException("该分享不存在....");
        }

        //防止已经积分兑换过得用户，会出现在此积分扣减的现象，在此需要查询中间表进行校验
        MidUserShare midUserShare = midUserShareDao.selectOne(MidUserShare.builder()
                .shareId(id)
                .userId(userId)
                .build());
        if (midUserShare != null) {
            return share;
        }

        UserDto userDto = this.userCenterFeignClinet.byIdUser(userId);
        if (peice > userDto.getBonus()) {
            throw new IllegalArgumentException("当前积分不充足....");
        }
        //3：记录兑换记录，往mid_user_share插入数据
        // TODO 使用feign完成数据的插入
        this.userCenterFeignClinet.addBonus(UserBonusDto
                .builder()
                //因为底层使用的是加法，用户兑换后积分肯定要减少，所以加负数等同于减
                .userId(userId)
                .bonus(0 - peice)
                .build());

        //将数据保存到中间表中
        this.midUserShareDao.insert(MidUserShare
                .builder()
                .userId(userId)
                .shareId(id)
                .build());
        return share;
    }


//    public static void main(String[] args) {
//        RestTemplate restTemplate = new RestTemplate();
//        ResponseEntity<String> forEntity = restTemplate.getForEntity("http://localhost:8001/user/{id}", String.class, 2);
//        System.out.println(forEntity);
//        //获取状态码
//        System.out.println(forEntity.getStatusCode());
//
//    }


}
